package com.gxc.service;

import com.alibaba.fastjson.JSONObject;
import com.gxc.dto.UploadModelZipResultDto;
import com.gxc.utils.CheckEmptyUtil;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.web.multipart.MultipartFile;

/**
 * 文件上传Service.
 *
 * @author 宫新程
 * @since 2019/3/6 14:12
 */
@Slf4j
@Service
public class UploadService {

  private static final String IO_CLOSE_EXCEPTION = "文件流关闭错误,{}";
  private static final String MAP_JSON_FILE_NAME_KEY = "fileName";

  @Value("${java.io.tmpdir}")
  private String tmpDir;

  /** 上传模板. */
  public UploadModelZipResultDto uploadModelZip(MultipartFile file) {
    UploadModelZipResultDto result = new UploadModelZipResultDto();

    // 获取上传的文件名称
    String zipFileName = Objects.requireNonNull(file.getOriginalFilename());
    File uploadFile = new File(tmpDir, zipFileName);
    try {
      // 将文件上传到临时文件目录.
      file.transferTo(uploadFile);
      // 获取临时文件的File对象
      File tmpFile = new File(tmpDir, zipFileName);
      // 读取zip中的文件.
      Map<String, String> map = readZipFile(tmpFile);

      // TODO：调用服务商接口.
      System.out.println(JSONObject.toJSONString(map));

      // 获取json文件全称
      String jsonFileName = map.get(MAP_JSON_FILE_NAME_KEY);
      result.setFileName(jsonFileName.substring(0, jsonFileName.lastIndexOf('.')));
      result.setModelJson(map.get(map.get(MAP_JSON_FILE_NAME_KEY)));

      // map中移除除图片无关的数据
      map.remove(MAP_JSON_FILE_NAME_KEY);
      map.remove(jsonFileName);
      result.setImages(map);
      result.setSuccess(true);
    } catch (IOException e) {
      log.error("文件上传到临时文件夹出现异常,{}", e);
      result.setSuccess(false);
      result.setErrorMsg("文件上传到临时文件夹出现异常");
      return result;
    }
    return result;
  }

  /**
   * 读取压缩文件中的内容.
   *
   * @param file 压缩文件.
   * @return 文件名:文件内容的map
   */
  private Map<String, String> readZipFile(File file) {
    ZipFile zf = null;
    InputStream in = null;
    ZipInputStream zin = null;
    Map<String, String> result = new HashMap<>();
    try {
      zf = new ZipFile(file);
      in = new BufferedInputStream(new FileInputStream(file));
      zin = new ZipInputStream(in);

      ZipEntry ze;
      while ((ze = zin.getNextEntry()) != null) {
        // 如果压缩文件中有目录, 错误
        if (ze.isDirectory() || ze.getSize() <= 0) {
          throw new RuntimeException("压缩文件中有文件夹");
        }
        // 如果是模板json文件, 则读取文件内容.
        if (ze.getName().lastIndexOf(".json") > 0) {
          BufferedReader reader = new BufferedReader(new InputStreamReader(zf.getInputStream(ze)));
          String line;
          StringBuilder builder = new StringBuilder();
          while ((line = reader.readLine()) != null) {
            builder.append(line);
          }
          reader.close();
          // 放入map中
          result.put(ze.getName(), builder.toString());
          // 目的是为了方便查询文件中的内容
          result.put(MAP_JSON_FILE_NAME_KEY, ze.getName());
        } else {
          // 将图片转换成base64串,inputStream 转换成 字节数组
          ByteArrayOutputStream bos = new ByteArrayOutputStream();
          InputStream inputStream = zf.getInputStream(ze);
          int len;
          byte[] bytes = new byte[1024];
          while ((len = inputStream.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
          }
          inputStream.close();
          bos.close();
          byte[] picBytes = bos.toByteArray();
          String baseStr = Base64Utils.encodeToString(picBytes);
          result.put(ze.getName(), baseStr);
        }
      }
    } catch (IOException e) {
      log.error("文件读取失败, {}", e);
      throw new RuntimeException("文件读取失败");
    } finally {
      if (CheckEmptyUtil.isNotEmpty(zin)) {
        try {
          zin.close();
        } catch (IOException e) {
          log.error(IO_CLOSE_EXCEPTION, e);
        }
      }
      if (CheckEmptyUtil.isNotEmpty(in)) {
        try {
          in.close();
        } catch (IOException e) {
          log.error(IO_CLOSE_EXCEPTION, e);
        }
      }
      if (CheckEmptyUtil.isNotEmpty(zf)) {
        try {
          zf.close();
        } catch (IOException e) {
          log.error(IO_CLOSE_EXCEPTION, e);
        }
      }
    }
    return result;
  }
}
